library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.std_logic_signed.ALL;
use IEEE.NUMERIC_STD.ALL;

entity shifter is
	PORT (
	OP: in std_logic_vector(1 downto 0); -- higher bit represents "right", lower bit represents 'logical'

	A: in std_logic_vector(31 downto 0);
	B: in std_logic_vector(31 downto 0);

	C: out std_logic_vector(31 downto 0);

	overflow_flag: out std_logic
	);
end shifter;

architecture Behavioral of shifter is
begin
	process(A, B, OP)
		variable fillup, flag: std_logic;
		variable flagv: std_logic_vector(16 downto 0);
		variable inp, outp: std_logic_vector(31 downto 0);
	begin
		-- fill up bits is the same as the arith/logic bit
		-- provided in opcode
		fillup := not OP(0) and A(31);
		inp := A;

		-- layer 1 multiplexers, reverse input order if necessary
		if (OP(1) = '1') then
			for i in A'RANGE loop
				inp(i) := A(31 - i);
			end loop;
		end if;

		outp := inp;

		flagv(0) := inp(31) and B(0);

		flag := flagv(0);

		-- layer 2 multilexers, left shift input by 1
		if (B(0) = '1') then
			for i in inp'RANGE loop
				if (i < 1) then
					outp(i) := fillup;
				else
					outp(i) := inp(i - 1);
				end if;
			end loop;
		end if;

		flagv(1) := outp(31) or outp(30);
		flagv(16) := flag;
		flag := flagv(16) or (flagv(1) and B(1));

		-- layer 3 multilexers, left shift input by 2
		if (B(1) = '1') then
			inp := outp;

			for i in inp'RANGE loop
				if (i < 2) then
					outp(i) := fillup;
				else
					outp(i) := inp(i - 2);
				end if;
			end loop;
		end if;

		flagv(0) := outp(31);
		for i in 1 to 3 loop
			flagv(i) := flagv(i - 1) or outp(31 - i);
		end loop;

		flagv(16) := flag;
		flag := flagv(16) or (flagv(3) and B(2));

		-- layer 4 multilexers, left shift input by 4
		if (B(2) = '1') then
			inp := outp;

			for i in inp'RANGE loop
				if (i < 4) then
					outp(i) := fillup;
				else
					outp(i) := inp(i - 4);
				end if;
			end loop;
		end if;

		flagv(0) := outp(31);
		for i in 1 to 7 loop
			flagv(i) := flagv(i - 1) or outp(31 - i);
		end loop;

		flagv(16) := flag;
		flag := flagv(16) or (flagv(7) and B(3));

		-- layer 5 multilexers, left shift input by 8
		if (B(3) = '1') then
			inp := outp;

			for i in inp'RANGE loop
				if (i < 8) then
					outp(i) := fillup;
				else
					outp(i) := inp(i - 8);
				end if;
			end loop;
		end if;

		flagv(0) := outp(31);
		for i in 1 to 15 loop
			flagv(i) := flagv(i - 1) or outp(31 - i);
		end loop;

		flagv(16) := flag;
		flag := flagv(16) or (flagv(15) and B(4));

		-- fourth layer multilexers, left shift input by 16
		if (B(4) = '1') then
			inp := outp;

			for i in inp'RANGE loop
				if (i < 16) then
					outp(i) := fillup;
				else
					outp(i) := inp(i - 16);
				end if;
			end loop;
		end if;

		-- last layer multiplexers, reverse input order if necessary
		if (OP(1) = '1') then
			inp := outp;

			for i in A'RANGE loop
				outp(i) := inp(31 - i);
			end loop;
		end if;

		C <= outp;
		overflow_flag <= flag and not op(1);
	end process;
end Behavioral;